package org.sdp.pearproject.config;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonToken;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.deser.ContextualDeserializer;
import com.fasterxml.jackson.databind.deser.std.NumberDeserializers;
import com.fasterxml.jackson.databind.deser.std.StringDeserializer;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.ser.std.BooleanSerializer;
import com.fasterxml.jackson.databind.type.TypeFactory;
import com.fasterxml.jackson.databind.util.Converter;
import lombok.SneakyThrows;
import org.springframework.boot.jackson.JsonComponent;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.util.StringUtils;

import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Optional;

@Configuration
public class JacksonConfig {

    public interface IEnum<V> {

        V getValue();

        static<T extends IEnum> T getBean(String value,Class<T> tClass){
            if (!StringUtils.hasText(value)){
                return null;
            }
            for (T enumObj : tClass.getEnumConstants()) {
                if (value.equals(enumObj.getValue().toString())) {
                    return enumObj;
                }
            }
            return null;
        }
        default String getStr(){
            return String.valueOf(getValue());
        }
    }

    @JsonComponent
    public static class MyEnumDeserializer<T extends IEnum> extends JsonDeserializer<T> implements ContextualDeserializer {

        private Class<T> targetClass = null;

        public MyEnumDeserializer() {
        }

        public MyEnumDeserializer(Class<T> targetClass) {
            this.targetClass = targetClass;
        }

        @Override
        public T deserialize(JsonParser p, DeserializationContext ctxt) {
            try {
                return IEnum.getBean(p.getText(),targetClass);
            } catch (IOException e) {
                e.printStackTrace();
            }
            return null;
        }

        @Override
        public JsonDeserializer<?> createContextual(DeserializationContext ctxt, BeanProperty property) {
            Class<T> targetClass = (Class<T>) ctxt.getContextualType().getRawClass();
            return new MyEnumDeserializer(targetClass);
        }
    }

    @JsonComponent
    public class MyEnumSerializer<T extends IEnum> extends JsonSerializer<T> {
        @Override
        public void serialize(T value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
            Optional<T> data = Optional.of(value);
            if (data.isPresent()) {
                gen.writeObject(data.get().getValue());
            }
        }
    }

    @JsonComponent
    public class MyDateDeserializer extends JsonDeserializer<Date> implements Converter<String, Date> {
        @Override
        public Date deserialize(JsonParser p, DeserializationContext ctxt) {
            try {
                return convert(p.getText());
            } catch (IOException e) {
                e.printStackTrace();
            }
            return null;
        }
        @SneakyThrows
        @Override
        public Date convert(String source) {
            if (!StringUtils.hasText(source)) {
                return null;
            }

            if (source.length() == 19) {
                SimpleDateFormat fullDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                return fullDateFormat.parse(source);
            } else if (source.length() == 10) {
                SimpleDateFormat fullDateFormat = new SimpleDateFormat("yyyy-MM-dd");
                return fullDateFormat.parse(source);
            } else {
                throw new Exception("invalid date:"+source);
            }
        }

        @Override
        public JavaType getInputType(TypeFactory typeFactory) {
            return typeFactory.constructType(String.class);
        }

        @Override
        public JavaType getOutputType(TypeFactory typeFactory) {
            return typeFactory.constructType(Date.class);
        }
    }

    @JsonComponent
    public class MyDateSerializer extends JsonSerializer<Date> implements Converter<Date,String> {
        @Override
        public void serialize(Date value, JsonGenerator gen, SerializerProvider serializers){
            try {
                gen.writeString(convert(value));
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        @Override
        public String convert(Date source) {
            if (source == null) {
                return null;
            }
            SimpleDateFormat fullDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            return fullDateFormat.format(source);
        }

        @Override
        public JavaType getInputType(TypeFactory typeFactory) {
            return typeFactory.constructType(Date.class);
        }

        @Override
        public JavaType getOutputType(TypeFactory typeFactory) {
            return typeFactory.constructType(String.class);
        }
    }

    @JsonComponent
    public class MyStringDeserializer extends StringDeserializer {

        public String deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
            //空数组可以被设置为null
            JsonToken t = p.currentToken();
            if (t == JsonToken.START_ARRAY) {
                t = p.nextToken();
                if (t == JsonToken.END_ARRAY) {
                    return this.getNullValue(ctxt);
                }
                return (String)this._deserializeFromArray(p, ctxt);
            }
            return super.deserialize(p, ctxt);
        }
    }

    @Bean
    public ObjectMapper objectMapper() {
        return createObjectMapper();
    }
    private ObjectMapper createObjectMapper(){
        ObjectMapper objectMapper = new ObjectMapper();
        SimpleModule simpleModule = new SimpleModule();

        simpleModule.addSerializer(IEnum.class, new MyEnumSerializer());
        simpleModule.addSerializer(Date.class, new MyDateSerializer());
        simpleModule.addSerializer(Boolean.class, new BooleanSerializer(true));

        objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);

        objectMapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true);

        objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        simpleModule.addDeserializer(String.class, new MyStringDeserializer());
        simpleModule.addDeserializer(Date.class, new MyDateDeserializer());
        simpleModule.addDeserializer(Enum.class, new MyEnumDeserializer());
        simpleModule.addDeserializer(Boolean.class, new NumberDeserializers.BooleanDeserializer(Boolean.class, null));
        objectMapper.registerModule(simpleModule);
        return objectMapper;
    }
}